#include "tst_json.h"
#include <QtTest/QtTest>
#include <QDebug>
#include <QString>
#include <vector>
#include <fstream>
#include <iostream>

void tst_json::tst_JsonCreate()
{

    /*******总结
     * * 读取的时候用特定的类型去接收对象，结果会被隐式转换
     */

    nlohmann::json j;

    // 生成“键值对”——————————————————————————————————
    j["pi"] = 3.141;
    j["happy"] = true;
    j["name"] = "Niels";
    j["nothing"] = nullptr;
    j["answer"]["everything"] = 42; // 初始化answer对象
    j["list"] = { 1, 0, 2 }; // 使用列表初始化的方法对"list"数组初始化
    j["object"] = { {"currency", "USD"}, {"value", 42.99} }; // 初始化object对象

    /* // 本质就是一颗Json树
{
  "pi": 3.141,
  "happy": true,
  "name": "Niels",
  "nothing": null,
  "answer": {
        "everything": 42
  },
  "list": [1, 0, 2],
  "object": {
        "currency": "USD",
        "value": 42.99
  }
}

*/




    // 读取“值”——————————————————————————————
    qDebug() << "读取“值”——————————————————————————————";

    float pi = j.at("pi");
    qDebug() << "用此方式获得一个Json值，值为3.141则正确 ：" << pi;

    bool happy = j.at("happy");
    qDebug() << "用此方式获得一个Json值，值为true则正确 ：" << happy;

    QString name = QString::fromStdString( j.at("name"));
    qDebug() << "用此方式获得一个Json值，值为 \"Niels\"则正确 ：" << name;

    QString currency = QString::fromStdString( j.at("object").at("currency"));
    qDebug() << "下级节点读取，值为 \"USD\"则正确 ：" << currency;

    qDebug() << "列表值读取,列表为[1, 0, 2] 如下打印列表：" ;
    std::vector<double> list = j.at("list");
    for(auto &i : list){
        qDebug() << i;
    }

    // error——————这里测试就没通过，因为键名为
    // json下没有任何键名为"currency"的节点，会引发error
    //    QString currency = QString::fromStdString( j.at("name").at("currency"));
    //    qDebug() << "多级读取，值为 \"USD\"则正确 ：" << currency;


    // dump返回json对象原始字符串
    qDebug() << "返回json的原始字符串：" << QString::fromStdString(j.dump());

}


// 显式转换
void tst_json::tst_ExplicitRead()
{
    nlohmann::json js = one_json_obj();
    qDebug() << "Json对象 显示转换为std::string " << QString::fromStdString(js.dump());
//    // 不建议 这样写
//    std::string s3 = js;
//    std::string s4;
//    s4 = js;

    // Booleans
//    nlohmann::json jb = one_json_obj();
//    auto b2 = jb.get<bool>();
//    qDebug() << "Json对象显式转换为bool 打印结果为 ：" << b2;
//    // 不建议
//    bool b3 = jb;
//    bool b4;
//    b4 = jb;

    //    // numbers
    //    int i = 42;
    //    json jn = i;
    //    auto f = jn.get<double>();
    //    // 不建议
    //    double f2 = jb;
    //    double f3;
    //    f3 = jb;
    //    // etc.
}



void tst_json::tst_String_serialization()
{
    // 反序列化：从字节序列恢复 JSON 对象。
    nlohmann::json j1 = "{\"happy\":true,\"pi\":3.141}"_json;
    auto j2 = R"({"happy":true,"pi":3.141})"_json;

    // 或者
    QString s = "{\"happy\":true,\"pi\":3.141}";
    nlohmann::json j3 = nlohmann::json::parse(s.toStdString().c_str());

    // 序列化：从 JSON 对象转化为字节序列。
    qDebug() << "j1: " << QString::fromStdString(j1.dump());
    qDebug() << "j2: " << QString::fromStdString(j2.dump());
    qDebug() << "j3: " << QString::fromStdString(j3.dump());

}




void tst_json::tst_Stream_serialization()
{
    // 从标准流中序列化、反序列化对象  ----  用上了再说吧，没调试好
    //    nlohmann::json j;
    //    std::cin  >> j; // 从标准输入中反序列化json对象
    //    std::cout << j; // 将json对象序列化到标准输出中
    //    qDebug() << "查看刚出的流js是否正常引入j对象，有值则正常： "
    //             << QString::fromStdString(j.dump());


    nlohmann::json j = this->one_json_obj();
    //将 json 对象序列化到本地文件，或者从存储在本地的文件中反序列化出 json 对象


    // 以易于查看的方式将json对象写入到本地文件
    std::ofstream o("D://pretty.json");
    o << std::setw(4) << j << std::endl;


    // 读取一个json文件，nlohmann会自动解析其中数据
    std::ifstream i("D://pretty.json");
    nlohmann::json jRead;
    i >> jRead;
    qDebug() << "从文件流中读取一个Json，有值则正常："
             << QString::fromStdString(jRead.dump());
}





using nlohmann::json;
namespace ns {
// 首先定义一个结构体
struct person {
    std::string name;
    std::string address;
    int age;
};

void to_json(json& j, const person& p) {
    j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};
}

void from_json(const json& j, person& p) {
    j.at("name").get_to(p.name);
    j.at("address").get_to(p.address);
    j.at("age").get_to(p.age);
}
}


void tst_json::tst_typeConversion()
{
    // 转换本质_______________________________________________________________________
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60}; // 定义初始化p

    // 从结构体转换到json对象
    nlohmann::json j;
    j["name"] = p.name;
    j["address"] = p.address;
    j["age"] = p.age;

    // 从json对象转换到结构体
    ns::person p2 {
        j["name"].get<std::string>(),
                j["address"].get<std::string>(),
                j["age"].get<int>()
    };


    // 通过接口转换___________________________________________________________
    /* 通过实现 from_json 与 to_json接口完成一个转换
       同时 注意 要在 nlohmann 的命名空间里   using nlohmann::json;
*/

    ns::person pT {"Ned Flanders", "744 Evergreen Terrace", 60};
    nlohmann::json jT = pT;
    // std::cout << jT << std::endl;
    qDebug() << "person对象转换为Json对象 有值就是成功："
             << QString::fromStdString(jT.dump());

    //    // {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}

    // conversion: json ->  person
    auto pJsonToNs = jT.get<ns::person>();

    // qDebug() << pJsonToNs == pT;
}






nlohmann::json tst_json::one_json_obj()
{
    nlohmann::json j;

    // 生成“键值对”——————————————————————————————————
    j["pi"] = 3.141;
    j["happy"] = true;
    j["name"] = "Niels";
    j["nothing"] = nullptr;
    j["answer"]["everything"] = 42; // 初始化answer对象
    j["list"] = { 1, 0, 2 }; // 使用列表初始化的方法对"list"数组初始化
    j["object"] = { {"currency", "USD"}, {"value", 42.99} }; // 初始化object对象

    return j;
}




QTEST_MAIN(tst_json)
